<template>
  <div class="page">
    <div class="bg-text">
      <div class="bg-text-l">
        <img src="./assets/images/icon-bg.png" alt="">
        <div class="bg-texts">ROS服务通信动画</div>
      </div>
    </div>
    <canvas id="canvas" ref="canvas"></canvas>
    <div class="main">
      <div class="content">
        <div class="content-l">
          <div class="body">
            <!-- service Client -->
            <template v-for="(item, index) in dataList.clientList" :key="index">
              <div v-if="item.isEmpyt" class="client-item-none"></div>
              <div v-else class="client-item">
                <div class="mask" v-show="maskDelet"></div>
                <div class="withe-radio" @click="handlePopup(item)">
                  <div class="client-title">servic Client <img style="width: 14px; height: 14px;margin-left: 8px;"
                      src="./assets/images/arr.png" alt=""></div>
                  <div style="color: #fff; font-size: 18px;margin-top: 0.8vh;">Node{{ index + 1 }}</div>
                  <!-- 弹出框 -->
                  <div class="topic-pupop" v-if="item.PopupClient">
                    <input class="topic-pupop-l" placeholder="请输入消息" type="text" v-model="item.pupopContent.content"
                      @input="limitInput(item.pupopContent, $event)" />
                    <div class="topic-pupop-text">（最多6个字符）</div>

                    <div class="topic-pupop-content" @click.stop="handleClosePopup(item, index, $event)">
                      <!-- <div class="topic-pupop-title">{{ item.pupopContent.clientTitle }}</div> -->
                      <div class="topic-pupop-title">REQUEST</div>
                      <div class="topic-pupop-r"></div>
                    </div>

                  </div>
                  <div class="delete_btn" v-show="maskDelet" @click="dataDelete(item, index)">
                    <img src="./assets/images/delete_ioc.png" alt="">
                  </div>
                </div>
              </div>
            </template>

            <!-- service Server -->
            <div class="server-item" ref="serverDom">
              <div class="server-item-title">{{ dataList.serverList.title }}</div>
              <div style="border-radius: 50%; border: 2px solid #fff; padding: 0.6vw;"> <img class="server-up-arrow"
                  src="./assets//images/send.png" alt="" style="width: 1.4vw; height: 1.4vw;"></div>
              <div class="line-server">Service Server</div>
              <div style="font-size: 16px;">Node5</div>
            </div>
          </div>
          <div class="header">
            <!-- 新增节点 -->
            <div class="addNode btn1" @click="addNode">
              <img class="header-ioc" src="./assets/images/add.png" alt="">
              <div class="header-text">新增</div>
            </div>
            <!-- 删除节点 -->
            <div class="deleteNode btn2" @click="deleteNode">
              <img class="header-ioc" src="./assets/images/delete.png" alt="">
              <div class="header-text">删除</div>
            </div>
            <!-- 编辑节点 -->
            <!-- <div class="editNode" @click="editNode">
        <img class="header-ioc" src="./assets/images/edit.png" alt="">
            <div class="header-text">编辑修改</div>
          </div> -->
            <!-- 复位节点 -->
            <div class="resetNode btn3" @click="resetNode">
              <img class="header-ioc" src="./assets/images/reset.png" alt="">
              <div class="header-text">复位</div>
            </div>
          </div>
        </div>
        <div class="letter n1" v-if="letterShow.includes(1)">✉</div>
        <div class="letter n2" v-if="letterShow.includes(1)">✉</div>
        <div class="letter n3" v-if="letterShow.includes(2)">✉</div>
        <div class="letter n4" v-if="letterShow.includes(2)">✉</div>
        <div class="letter n5" v-if="letterShow.includes(3)">✉</div>
        <div class="letter n6" v-if="letterShow.includes(3)">✉</div>
        <div class="letter n7" v-if="letterShow.includes(4)">✉</div>
        <div class="letter n8" v-if="letterShow.includes(4)">✉</div>
      </div>

      <div class="content-r">
        <div class="popup-response">
          <!-- <div class="rules">
            <div class="rule-title">Response规则</div>
            <div class="bg-rule">
              <div class="title-box">
                <div class="title-request">REQUEST</div>
                <div class="title-response">RESPONSE</div>
              </div>
              <div class="rule-contentList">
                <div class="rule-item" v-for="(el, index) in dataList.responseList">
                  <input class="item-request" type="text" v-model="el.request" @input="limitRule(el, 'request')"
                    @blur="checkDuplicate('request', el.request, index, el.originalRequest)" />
                  <input class="item-response" type="text" v-model="el.response" @input="limitRule(el, 'response')"
                    @blur="checkDuplicate('response', el.response, index, el.originalResponse)" />
                </div>
                <div class="rule-item">
                  <div class="item-request">(未定义字符)</div>
                  <div class="item-response">404</div>
                </div>
              </div>
            </div>
          </div> -->
          <div class="message-record">
            <div class="message-title">表单数据</div>
            <div class="bg-message">
              <div class="message-index"></div>
              <div class="message-box">
                <div class="message-title-node">NODE</div>
                <div class="message-title-request">REQUEST</div>
                <div class="message-title-response">RESPONSE</div>
              </div>
              <div class="message-contentList">
                <div class="message-item" v-for="(el, index) in dataList.messageList">
                  <div class="message-item-node">{{ el.node }}</div>
                  <div class="message-item-request">{{ el.request }}</div>
                  <div class="message-item-response">{{ el.response }}</div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="less">
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
}

@import './app.less';
</style>

<script setup>
import { ref, onMounted, onUnmounted, reactive, nextTick, watch } from 'vue';

const letterShow = ref([])
const maxNodes = 4;
const number = ref(1)


const dataList = reactive({
  // 客户端数据
  clientList: [
    // {
    //   title: 'Request',
    //   descriptionOne: 'Service',
    //   descriptionTow: 'Client',
    //   PopupClient: false,  // 控制弹窗
    //   isEmpyt: false,  // 判断当前节点是否被删除
    //   pupopContent: {  // 弹窗内容
    //     clientTitle: 'Client1', content: 'abcd'
    //   }
    // },

  ],

  // 服务端数据
  serverList: { title: 'Respones', descriptionOne: 'Service', descriptionTow: 'Server' },

  // Response规则
  responseList: [
    { request: '111', response: 'Fire', originalRequest: '111', originalResponse: 'Fire' },
    { request: 'aaa', response: 'Stand by', originalRequest: 'aaa', originalResponse: 'Stand by' },
    { request: 'abc', response: 'Retreat', originalRequest: 'abc', originalResponse: 'Retreat' },
    // { request: '(未定义字符)', response: '404' },
  ],


  // message 消息记录
  messageList: [],

  // request请求线
  requestLine: [],

  // response请求线
  responsetLine: [],
})
let ctx
let dashOffset = 0
let animationFrameId

/**
 * 获取服务端dom元素
 */
const serverDom = ref(null)

/**
 * 将Response规则转换成键值对
 */
const responseMap = ref([])
responseMap.value = dataList.responseList.reduce((acc, item) => {
  if (item.request !== '(未定义字符)') {
    acc[item.request] = item.response;
  }
  return acc;
}, {});

watch(() => dataList.responseList, (newValue) => {
  responseMap.value = newValue.reduce((acc, item) => {
    if (item.request !== '(未定义字符)') {
      acc[item.request] = item.response;
    }
    return acc;
  }, {})
}, { deep: true })

/**
 * 检查是否有相同的字符串
 * @param type 
 * @param value 
 * @param index 
 */
// const checkDuplicate = (type, value, index, originalValue) => {
//   const list = type === 'request' ? dataList.responseList.map(item => item.request) : dataList.responseList.map(item => item.response);
//   for (let i = 0; i < list.length; i++) {
//     if (i !== index && list[i] === value) {
//       alert(`已有相同的 ${type}: ${value}`);
//       if (type === 'request') {
//         dataList.responseList[index].request = originalValue;
//       } else {
//         dataList.responseList[index].response = originalValue;
//       }
//       return;
//     }
//   }
// }

const checkDuplicate = (type, value, index, originalValue) => {
  if (!dataList || !dataList.responseList || index < 0 || index >= dataList.responseList.length) {
    console.error('Invalid dataList or index');
    return;
  }

  const list = dataList.responseList.map(item => item[type]);

  for (let i = 0; i < list.length; i++) {
    if (i !== index && list[i] === value) {
      alert(`已有相同的 ${type}: ${value}`);
      dataList.responseList[index][type] = originalValue;
      return;
    }
  }
}


/**
 * 发布画线
 * @param {DOMRect} rect1 第一个DOM元素的边界矩形
 * @param {DOMRect} rect2 第二个DOM元素的边界矩形
 * @param {string} name 标识符
 */
// 请求
// const LocationRequset = [
//   { x1: 30, x2: -70, y1: 50, y2: 0 },
//   { x1: -50, x2: 60, y1: 40, y2: -30 },
//   { x1: 60, x2: -50, y1: -10, y2: 60 },
//   { x1: -50, x2: 55, y1: -40, y2: 20 },
// ]
// // 响应
// const LocationResponse = [
//   { x1: -60, x2: 60, y1: -30, y2: 30 },
//   { x1: 60, x2: -40, y1: 0, y2: 70 },
//   { x1: -55, x2: 50, y1: 25, y2: -45 },
//   { x1: 45, x2: -65, y1: 50, y2: -10 },
// ]

// const LocationRequset = [
//   { x1: 0.03, x2: -0.07, y1: 0.05, y2: 0 },
//   { x1: -0.05, x2: 0.06, y1: 0.04, y2: -0.03 },
//   { x1: 0.06, x2: -0.05, y1: -0.01, y2: 0.06 },
//   { x1: -0.05, x2: 0.055, y1: -0.04, y2: 0.02 },
// ];

// const LocationResponse = [
//   { x1: -0.06, x2: 0.06, y1: -0.03, y2: 0.03 },
//   { x1: 0.06, x2: -0.04, y1: 0, y2: 0.07 },
//   { x1: -0.055, x2: 0.05, y1: 0.025, y2: -0.045 },
//   { x1: 0.045, x2: -0.065, y1: 0.05, y2: -0.01 },
// ];

const LocationRequset = [
  { x1: 0.02, x2: -0.04, y1: 0.03, y2: 0 },
  { x1: -0.04, x2: 0.035, y1: 0.01, y2: -0.025 },
  { x1: 0.04, x2: -0.03, y1: -0.01, y2: 0.035 },
  { x1: -0.03, x2: 0.04, y1: -0.03, y2: 0.02 },
];

const LocationResponse = [
  { x1: -0.035, x2: 0.04, y1: -0.025, y2: 0.01 },
  { x1: 0.05, x2: -0.03, y1: -0.001, y2: 0.04 },
  { x1: -0.04, x2: 0.025, y1: 0.015, y2: -0.03 },
  { x1: 0.025, x2: -0.04, y1: 0.035, y2: -0.01 },
];

const drawLinePub = (rect1, rect2, currentIndex, name) => {

  let x1, x2, y1, y2;

  // if (name == 'request') {
  //   x1 = rect1.x + (rect1.width / 2) + LocationRequset[currentIndex].x1;
  //   x2 = rect2.x + (rect2.width / 2) + LocationRequset[currentIndex].x2;

  //   // 找到中心点
  //   y1 = rect1.y + (rect1.height / 2) + LocationRequset[currentIndex].y1;
  //   y2 = rect2.y + (rect2.height / 2) + LocationRequset[currentIndex].y2;
  // } else {
  //   x1 = rect1.x + (rect1.width / 2) + LocationResponse[currentIndex].x1;
  //   x2 = rect2.x + (rect2.width / 2) + LocationResponse[currentIndex].x2;

  //   // 找到中心点
  //   y1 = rect1.y + (rect1.height / 2) + LocationResponse[currentIndex].y1;
  //   y2 = rect2.y + (rect2.height / 2) + LocationResponse[currentIndex].y2;
  // }

  if (name == 'request') {
    x1 = rect1.x + (rect1.width / 2) + window.innerWidth * LocationRequset[currentIndex].x1;
    x2 = rect2.x + (rect2.width / 2) + window.innerWidth * LocationRequset[currentIndex].x2;

    y1 = rect1.y + (rect1.height / 2) + window.innerWidth * LocationRequset[currentIndex].y1;
    y2 = rect2.y + (rect2.height / 2) + window.innerWidth * LocationRequset[currentIndex].y2;
  } else {
    x1 = rect1.x + (rect1.width / 2) + window.innerWidth * LocationResponse[currentIndex].x1;
    x2 = rect2.x + (rect2.width / 2) + window.innerWidth * LocationResponse[currentIndex].x2;

    y1 = rect1.y + (rect1.height / 2) + window.innerWidth * LocationResponse[currentIndex].y1;
    y2 = rect2.y + (rect2.height / 2) + window.innerWidth * LocationResponse[currentIndex].y2;
  }




  // 计算控制点
  const ctrlX1 = x1;
  const ctrlY1 = y1 + (y2 - y1);
  const ctrlX2 = x2;
  const ctrlY2 = y2 - (y2 - y1);

  ctx.beginPath();
  ctx.setLineDash([5, 5]);
  ctx.moveTo(x1, y1);
  // 画直线
  // ctx.lineTo(x2, y2);

  // 画曲线 （使用第一个控制点 (ctrlX1, ctrlY1) 和第二个控制点 (ctrlX2, ctrlY2) 来定义曲线的形状。 曲线最终到达终点 (x2, y2)）
  // ctx.bezierCurveTo(ctrlX1, ctrlY1, ctrlX2, ctrlY2, x2, y2);
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = name == 'request' ? '#54b0d7' : '#e2a124';
  ctx.lineWidth = 1;
  ctx.stroke();

};

/**
 * @return 每一帧重现画，动画
 */
const animateLine = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.setLineDash([5, 10]);
  ctx.lineDashOffset = -dashOffset;

  // if (dataList.arrayList.length === 2) {
  //   drawLine(dataList.arrayList[0], dataList.arrayList[1]);
  // }

  // 绘制所有线条
  dataList.requestLine.forEach(line => {
    drawLinePub(line.rect1, line.rect2, line.currentIndex, 'request');
  });


  dataList.responsetLine.forEach(line => {
    drawLinePub(line.rect1, line.rect2, line.currentIndex, 'response');
  });


  dashOffset += 1;
  animationFrameId = requestAnimationFrame(animateLine);
};


/**
 * 删除节点
 */
const maskDelet = ref(false) //控制蒙层
const deleteNode = () => {
  // 判断当前clientList是否为空
  const flag = dataList.clientList.filter(el => el.isEmpyt !== true)
  if (flag.length == 0) {
    return alert('当前没有节点,请新增节点!')
  }

  maskDelet.value = !maskDelet.value
}

/**
 * 新增节点
 */
const addNode = () => {
  maskDelet.value = false
  // if (maskDelet.value) return alert('当前在删除节点')
  const newNode = {
    title: 'Request',
    descriptionOne: 'Service Client',
    descriptionTow: 'Client',
    PopupClient: false,  // 控制弹窗
    isEmpyt: false,  // 判断当前节点是否被删除
    pupopContent: {  // 弹窗内容
      clientTitle: `Node${number.value}`, content: ''
    },
    unique: Math.random()
  }
  // 检查 dataList.clientList 中是否有 isEmpyt 为 true 的
  const emptyIndex = dataList.clientList.findIndex(item => item.isEmpyt === true);
  if (emptyIndex !== -1) {
    dataList.clientList[emptyIndex] = newNode;
    number.value++
    return;
  }
  // 计算总节点
  const totalLength = dataList.clientList.length;
  // 小于4个新增
  if (totalLength < maxNodes) {
    dataList.clientList.push(newNode)
    number.value++
  } else {
    alert(`最多只能添加4个`);
  }
}


/**
 * 删除当前点击节点
 */
const dataDelete = (item, currentIndex) => {
  item.isEmpyt = true
  // 删除当前的节点中所有关联的线条也删除
  dataList.requestLine = dataList.requestLine.filter(el => item.unique !== el.unique)
  dataList.responsetLine = dataList.responsetLine.filter(el => item.unique !== el.unique)

  const regex = /\d+/;
  const match = item.pupopContent.clientTitle.match(regex);
  letterShow.value = letterShow.value.filter(el => el !== Number(match[0]))
  number.value = Number(match[0])
}
/**
 * 清除
 */
const resetNode = () => {
  dataList.messageList = []
  dataList.requestLine = []
  dataList.responsetLine = []
  dataList.clientList = []
  maskDelet.value = false
  number.value = 1
  letterShow.value = []
  // 插入8条空消息
  for (let i = 0; i < 8; i++) {
    dataList.messageList.push({ node: '', request: '', response: '' })
  }
}

// 插入8条空消息
for (let i = 0; i < 8; i++) {
  dataList.messageList.push({ node: '', request: '', response: '' })
}

/**
 * 限制6个字符
 */
const limitInput = (el, event) => {
  el.content = el.content.slice(0, 6);
};


/**
 * response规则修改不超过六个字符
 */
const limitRule = (el, name) => {
  el[name] = el[name].slice(0, 9);
}

/**
 * 关闭弹窗 发送给服务端
 * @param el 
 */
const handleClosePopup = (el, index, event) => {
  if (!el.pupopContent.content) {
    return alert('请输入消息')
  }
  // request 加入画线数组
  dataList.requestLine.push({
    rect1: event.currentTarget.parentNode.parentNode.parentNode.getBoundingClientRect(),
    rect2: serverDom.value.getBoundingClientRect(),
    unique: el.unique,
    currentIndex: index,
    currentDom: event.currentTarget.parentNode.parentNode.parentNode,
    responseDom: serverDom.value
  })
  letterShow.value.push(index + 1)
  // response 加入画线数组
  dataList.responsetLine.push({
    rect1: serverDom.value.getBoundingClientRect(),
    rect2: event.currentTarget.parentNode.parentNode.parentNode.getBoundingClientRect(),
    unique: el.unique,
    currentIndex: index,
    currentDom: event.currentTarget.parentNode.parentNode.parentNode,
    responseDom: serverDom.value
  })

  // 相对于映射表取值
  const responseValue = responseMap.value[el.pupopContent.content] ? responseMap.value[el.pupopContent.content] : '404'

  // 添加到消息记录中
  // 最多8条
  if (dataList.messageList.length >= 8) {
    dataList.messageList.pop()
  }
  dataList.messageList.unshift({ node: el.pupopContent.clientTitle, request: el.pupopContent.content, response: `n${el.pupopContent.content}` })
  // 画线
  cancelAnimationFrame(animationFrameId);
  animateLine();
  el.pupopContent.content = ''
  // 关闭弹窗
  el.PopupClient = false
}


/**
 * 打开topic弹窗
 */
const handlePopup = (el) => {
  dataList.clientList.forEach(item => item.PopupClient = false)
  el.PopupClient = true
}

onMounted(() => {
  const canvas = document.getElementById('canvas');
  canvas.width = window.innerWidth; // 更新Canvas宽度为窗口宽度
  canvas.height = window.innerHeight; // 更新Canvas高度为窗口高度
  ctx = canvas.getContext('2d');



  // 监听窗口大小变化事件，重新绘制连线
  window.addEventListener('resize', () => {
    canvas.width = window.innerWidth; // 更新Canvas宽度为窗口宽度
    canvas.height = window.innerHeight; // 更新Canvas高度为窗口高度


    // 获取所有需要监听的DOM元素
    const clientItem = document.querySelectorAll('.client-item');
    const serverDom = document.querySelector('.server-item');

    const resizeObserverLine = new ResizeObserver(entries => {
      entries.forEach(entry => {
        // 获取DOM元素的新尺寸
        const { width, height } = entry.contentRect;
        const rect = entry.target.getBoundingClientRect();
        // 更新线条位置
        dataList.requestLine.forEach(line => {
          if (line.currentDom === entry.target) {
            line.rect1.x = rect.left;
            line.rect1.y = rect.top;
            line.rect1.height = height;
            line.rect1.width = width;
          }
        });

        // 更新线条位置
        dataList.responsetLine.forEach(line => {
          if (line.currentDom === entry.target) {
            line.rect2.x = rect.left;
            line.rect2.y = rect.top;
            line.rect2.height = height;
            line.rect2.width = width
          }
        });
      });
    })

    const resizeObserverServe = new ResizeObserver(entries => {
      // 获取DOM元素的新尺寸
      const { width, height } = entries[0].contentRect;
      const rect = entries[0].target.getBoundingClientRect();
      // // 更新线条位置
      dataList.requestLine.forEach(line => {
        if (line.responseDom === entries[0].target) {
          line.rect2.x = rect.left;
          line.rect2.y = rect.top;
          line.rect2.height = height;
          line.rect2.width = width;
        }
      });

      // 更新线条位置
      dataList.responsetLine.forEach(line => {
        if (line.responseDom === entries[0].target) {
          line.rect1.x = rect.left;
          line.rect1.y = rect.top;
          line.rect1.height = height;
          line.rect1.width = width
        }
      });

    })


    // 开始监听所有DOM元素
    clientItem.forEach(element => {
      resizeObserverLine.observe(element);
    });

    resizeObserverServe.observe(serverDom)

  })


});

onUnmounted(() => {
  cancelAnimationFrame(animationFrameId);
});

</script>
